<template lang="pug">
  div(class="tabs")
    div(class="tabs-titles",ref="titles")
      div(class="title-item",@click="select(index)",v-for="(item, index) in tabs",:class="{ 'active': index === tabIndex }" :key="item.label") {{item.label}}
      div(class="scroll-bar", ref="scrollBar",:style="{width: `${tabWidth}px`,transform: `translate3d(${tabIndex * tabWidth}px, 0, 0)`}")
    div(class="tabs-content", ref="content")
      div(class="wrapper" :style="{transform: `translate3d(${translateX - tabIndex * width}px, 0, 0)`, transition: touching ? '' : 'transform .3s'}")
        slot
</template>

<script>
import Vue from "vue";

export default {
  name: "vue-slide-tabs",
  props: {
    tabs: { type: Array, default: () => [] },
    scrollable: { type: Boolean, default: true }
  },
  data() {
    return {
      tabIndex: 0,
      tabWidth: 0,
      touching: false,
      dx: 0,
      translateX: 0,
      width: 0
    };
  },
  methods: {
    getAngle(dx, dy) {
      return (360 * Math.atan(dy / dx)) / (2 * Math.PI);
    },
    select(index) {
      this.tabIndex = index;
    },
    touchstart(e) {
      if (!this.scrollable) return;
      this.touching = true;

      this.startX = e.touches[0].pageX;
      this.startY = e.touches[0].pageY;
      this.endX = e.touches[0].pageX;
      this.endY = e.touches[0].pageY;
    },
    touchmove(e) {
      if (!this.scrollable || !this.touching) return;

      const endX = e.touches ? e.touches[0].pageX : e.pageX; // e.touches无数据，则表示鼠标移动事件
      const endY = e.touches ? e.touches[0].pageY : e.pageY;

      const dx = (this.dx = endX - this.startX); // 横向移动距离

      if (this.canSlider()) {
        return;
      }

      if (
        Math.abs(dx) > 6 &&
        Math.abs(this.getAngle(dx, endY - this.startY)) < 30
      ) {
        this.translateX = dx; // 横向移动距离
      }
    },
    touchend() {
      if(!this.scrollable) return;

      this.touching = false;
      this.translateX = 0;
      const percent = this.dx / this.width;

      if (this.canSlider()) {
        return;
      }

      if (percent < -0.3) {
        this.tabIndex++;
      }

      if (percent > 0.3) {
        this.tabIndex--;
      }
    },
    canSlider() {
      return (
        (this.dx < 0 && this.tabIndex >= this.tabs.length - 1) ||
        (this.dx > 0 && this.tabIndex === 0)
      );
    },
    resizeWidth() {
      this.width = this.$el.clientWidth;
      this.tabWidth = Math.round(this.width / this.tabs.length);
    },
    bindTouchEvents() {
      this.$nextTick(() => {
        // todo: touch事件和鼠标事件只要一组，需要优化
        const ref = this.$refs.content;
        ref.addEventListener("touchstart", this.touchstart);
        ref.addEventListener("touchmove", this.touchmove);
        ref.addEventListener("touchend", this.touchend);

        ref.addEventListener("mousedown", this.touchstart);
        ref.addEventListener("mousemove", this.touchmove);
        ref.addEventListener("mouseup", this.touchend);
      });
    }
  },
  watch: {
    tabIndex(newVal) {
      this.$emit("change", newVal);
    }
  },
  mounted() {
    this.resizeWidth();
    this.bindTouchEvents();

    window.addEventListener("resize", this.resizeWidth);
  }
};
</script>

<style lang="less" scoped>
@color-border : #eee;
@color-theme : rgb(212, 68, 57);
@font-size-base : 14px;

.tabs {
  &-titles {
    position: relative;
    display: flex;
    line-height: 44px;
    text-align: center;

    &::after {
      content: "";
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      border-bottom: 1px solid @color-border;
      transform: translateY(0.5);
    }
  }

  .scroll-bar {
    position: absolute;
    bottom: 0;
    z-index: 1;
    border-bottom: 2px solid @color-theme;
    border-radius: 5px;
    transition: transform 0.3s;
  }

  .title-item {
    flex: 1;
  }

  .active {
    color: @color-theme;
  }

  &-content {
    height: calc(100% - 44px);
    overflow: hidden;

    .wrapper {
      font-size: 0;
      white-space: nowrap;

      & > div {
        display: inline-block;
        width: 100%;
        size: 100%;
        font-size: @font-size-base;
      }
    }
  }
}
</style>